/*!
 * @author  Vladimir Kerimov
 * @file    text.h
 * @version 1.0
 * @brief   Text of Unicode with encode/decode
 * @date    2013
 */

/*
 *    ______   __
 *   /  ___/  /__/   __  ___      _____        _____     __    __     _____        __  ___      _____     __  ___
 *  |  |___    __   /  |/   \    / __  \      /  _  \   |  |  |  |   /  _  \      |  |/   \    /  _  \   |  |/   \
 *   \___  \  /  |  |   __   |  | /  __/     |  / \  |  |  |  |  |  |  / \  |     |   __   |  |  / \  |  |   __   |
 *  ____/  /  |  |  |  /  \  |  |   /___     |  \_/  |  |   \_|  |  |  \_/  |     |  /  \  |  |  \_/  |  |  /  \  |
 * /______/   |__/  |__|  |__/   \_____/      \___   |   \___/|__|   \___/|_|     |__|  |__|   \_____/   |__|  |__|
 *                                                |  |
 *                                                |__/
 *
 */

#pragma once

#include <qua/api.h>
#include <qua/lazy.h>
#include <qua/stdfwd.h>

namespace qua
{
    class QUAZAR_API text
    {
    public:
        class data;

        text();
        ~text();

        text( const wchar_t* wide_str );
        text( const char* utf8_str );
        text( const char* enc_str, const char* encoding );

        text( std::wstring const& wide_str );
        text( std::string const& utf8_str );
        text( std::string const& enc_str, std::string const& encoding );

        text( wchar_t wide_char );
        text( char ascii_char );
        text( char enc_char, const char* encoding );

        const wchar_t* to_wide() const;
        const char* to_utf8() const;
        const char* to_encoding( const char* encoding ) const;
        const char* to_encoding( std::string const& encoding ) const;

        operator const wchar_t* () const;

        wchar_t& operator [] ( int id );
        wchar_t  operator [] ( int id ) const;

        void from_wide( const wchar_t* wide_str );
        void from_utf8( char const* utf8_str );
        void from_encoding( const char* enc_str, const char* encoding );

        void from_wide( std::wstring const& wide_str );
        void from_utf8( std::string const& utf8_str );
        void from_encoding( std::string const& enc_str, std::string const& encoding );

        text& operator = ( const wchar_t* wide_str );
        text& operator = ( char const* utf8_str );

        text& operator = ( std::wstring const& wide_str );
        text& operator = ( std::string const& utf8_str );

        text& operator = ( wchar_t wide_char );
        text& operator = ( char ascii_char );

        class iterator;
        class const_iterator;
        class reverse_iterator;
        class const_reverse_iterator;

        iterator begin();
        iterator end();

        const_iterator begin() const;
        const_iterator end() const;

        reverse_iterator rbegin();
        reverse_iterator rend();

        const_reverse_iterator rbegin() const;
        const_reverse_iterator rend() const;

        const_iterator cbegin() const;
        const_iterator cend() const;

        const_reverse_iterator crbegin() const;
        const_reverse_iterator crend() const;

              iterator find( wchar_t value );
        const_iterator find( wchar_t value ) const;

              reverse_iterator rfind( wchar_t value );
        const_reverse_iterator rfind( wchar_t value ) const;

        wchar_t& front();
        wchar_t  front() const;

        wchar_t& back();
        wchar_t  back() const;

        int    length() const;
        size_t size()   const;
        bool   empty()  const;

        void resize( size_t new_size );
        void reserve( size_t reserve_size );
        void clear();

        text operator + ( text const& other ) const;
        text operator + ( wchar_t wide_char ) const;
        text operator + ( char ascii_char ) const;

        inline text operator + ( const wchar_t* wide_str ) const;
        inline text operator + ( const char* utf8_str ) const;

        friend QUAZAR_API text operator + ( wchar_t wide_char, text const& right );
        friend QUAZAR_API text operator + ( char ascii_char, text const& right );

        friend inline text operator + ( const wchar_t* wide_str, text const& right );
        friend inline text operator + ( const char* utf8_str, text const& right );

        text& operator += ( text const& other );
        text& operator += ( wchar_t wide_char );
        text& operator += ( char ascii_char );

        void insert( const_iterator const& into, text const& ins_str );
        void replace( text const& repl_str, text const& ins_str );
        void erase( const_iterator const& from, const_iterator const& upto );

        text substr( const_iterator const& from, const_iterator const& upto ) const;

        const wchar_t* c_str() const;

        bool operator == ( text const& other ) const;
        bool operator != ( text const& other ) const;
        bool operator <= ( text const& other ) const;
        bool operator >= ( text const& other ) const;
        bool operator <  ( text const& other ) const;
        bool operator >  ( text const& other ) const;

        inline bool operator == ( const char* other ) const;
        inline bool operator != ( const char* other ) const;
        inline bool operator <= ( const char* other ) const;
        inline bool operator >= ( const char* other ) const;
        inline bool operator <  ( const char* other ) const;
        inline bool operator >  ( const char* other ) const;

        inline bool operator == ( const wchar_t* other ) const;
        inline bool operator != ( const wchar_t* other ) const;
        inline bool operator <= ( const wchar_t* other ) const;
        inline bool operator >= ( const wchar_t* other ) const;
        inline bool operator <  ( const wchar_t* other ) const;
        inline bool operator >  ( const wchar_t* other ) const;

        void push_back( wchar_t wide_char );
        void push_back( char ascii_char );

        template< class EachFunctor >
        void each( EachFunctor const& func ) const;

        template< class EachFunctor >
        void each( EachFunctor const& func );

        bool is_wide() const;
        bool is_utf8() const;
        bool is_encoded() const;

        const char* get_encoding() const;

    private:
        lazy<data> m_data;

        text( lazy<data>&& temp );
        text( data* data_ptr );
    };

    inline bool operator == ( const char* other, text const& right );
    inline bool operator != ( const char* other, text const& right );
    inline bool operator <= ( const char* other, text const& right );
    inline bool operator >= ( const char* other, text const& right );
    inline bool operator <  ( const char* other, text const& right );
    inline bool operator >  ( const char* other, text const& right );

    inline bool operator == ( const wchar_t* other, text const& right );
    inline bool operator != ( const wchar_t* other, text const& right );
    inline bool operator <= ( const wchar_t* other, text const& right );
    inline bool operator >= ( const wchar_t* other, text const& right );
    inline bool operator <  ( const wchar_t* other, text const& right );
    inline bool operator >  ( const wchar_t* other, text const& right );

    class text::const_iterator
    {
    public:
        typedef std::random_access_iterator_tag iterator_category;
	    typedef wchar_t value_type;
	    typedef int difference_type;
	    typedef int distance_type;
	    typedef wchar_t* pointer;
	    typedef wchar_t& reference;

        inline const_iterator();
        inline const_iterator( text const* container, int index );

        virtual ~const_iterator() {
        }

        inline const_iterator& operator ++ ();
        inline const_iterator& operator -- ();

        inline const_iterator operator ++ ( int );
        inline const_iterator operator -- ( int );

        inline const_iterator& operator += ( int offset );
        inline const_iterator& operator -= ( int offset );

        inline const_iterator operator + ( int offset ) const;
        inline const_iterator operator - ( int offset ) const;

        inline wchar_t operator * () const;
        inline wchar_t operator [] ( int offset ) const;

        inline bool operator == ( const_iterator const& other ) const;
        inline bool operator != ( const_iterator const& other ) const;
        inline bool operator <= ( const_iterator const& other ) const;
        inline bool operator >= ( const_iterator const& other ) const;
        inline bool operator <  ( const_iterator const& other ) const;
        inline bool operator >  ( const_iterator const& other ) const;

        friend inline int operator - ( text::const_iterator const& left, text::const_iterator const& right );
        friend inline text::const_iterator operator + ( int offset, text::const_iterator const& iter );

        inline text::const_reverse_iterator reverse() const;

        inline virtual int index() const;

    protected:
        int m_index;
        text const* m_container;
    };

    class text::iterator : public text::const_iterator
    {
    public:
        typedef text::const_iterator base;

        inline iterator();
        inline iterator( text* container, int index );

        inline wchar_t& operator * ();
        inline wchar_t& operator [] ( int offset );

        inline text::reverse_iterator reverse() const;

    private:
        text* m_nonconst;
    };

    class text::const_reverse_iterator : public text::const_iterator
    {
    public:
        typedef text::const_iterator base;

        inline const_reverse_iterator();
        inline const_reverse_iterator( text const* container, int index );

        inline wchar_t operator * () const;
        inline wchar_t operator [] ( int offset ) const;

        inline text::const_iterator reverse() const;

        inline virtual int index() const override;
    };

    class text::reverse_iterator : public text::const_reverse_iterator
    {
    public:
        typedef text::const_reverse_iterator base;

        inline reverse_iterator();
        inline reverse_iterator( text* container, int index );

        inline wchar_t& operator * ();
        inline wchar_t& operator [] ( int offset );

        inline text::iterator reverse() const;

    private:
        text* m_nonconst;
    };

    QUAZAR_API void assert_text_iterator_container_is_initialized( text const* container );
    QUAZAR_API void assert_text_iterator_container_match( text const* first, text const* second );

    // -- inline -- //

    template< class EachFunctor >
    void text::each( EachFunctor const& func ) const
    {
        for( const_iterator it = begin(); it != end(); ++it ) func( *it );
    }

    template< class EachFunctor >
    void text::each( EachFunctor const& func )
    {
        for( iterator it = begin(); it != end(); ++it ) func( *it );
    }

    inline text text::operator + ( const wchar_t* wide_str ) const
    {
        return *this + text(wide_str);
    }

    inline text text::operator + ( const char* utf8_str ) const
    {
        return *this + text(utf8_str);
    }

    inline text operator + ( const wchar_t* wide_str, text const& right )
    {
        return text(wide_str) + right;
    }

    inline text operator + ( const char* utf8_str, text const& right )
    {
        return text(utf8_str) + right;
    }

    inline bool text::operator == ( const char* other ) const
    {
        return *this == text(other);
    }

    inline bool text::operator != ( const char* other ) const
    {
        return *this != text(other);
    }

    inline bool text::operator <= ( const char* other ) const
    {
        return *this <= text(other);
    }

    inline bool text::operator >= ( const char* other ) const
    {
        return *this >= text(other);
    }

    inline bool text::operator <  ( const char* other ) const
    {
        return *this < text(other);
    }

    inline bool text::operator >  ( const char* other ) const
    {
        return *this > text(other);
    }

    inline bool text::operator == ( const wchar_t* other ) const
    {
        return *this == text(other);
    }

    inline bool text::operator != ( const wchar_t* other ) const
    {
        return *this != text(other);
    }

    inline bool text::operator <= ( const wchar_t* other ) const
    {
        return *this <= text(other);
    }

    inline bool text::operator >= ( const wchar_t* other ) const
    {
        return *this >= text(other);
    }

    inline bool text::operator <  ( const wchar_t* other ) const
    {
        return *this < text(other);
    }

    inline bool text::operator >  ( const wchar_t* other ) const
    {
        return *this > text(other);
    }

    inline bool operator == ( const char* other, text const& right )
    {
        return text(other) == right;
    }

    inline bool operator != ( const char* other, text const& right )
    {
        return text(other) != right;
    }

    inline bool operator <= ( const char* other, text const& right )
    {
        return text(other) <= right;
    }

    inline bool operator >= ( const char* other, text const& right )
    {
        return text(other) >= right;
    }

    inline bool operator < ( const char* other, text const& right )
    {
        return text(other) < right;
    }

    inline bool operator > ( const char* other, text const& right )
    {
        return text(other) > right;
    }

    inline bool operator == ( const wchar_t* other, text const& right )
    {
        return text(other) == right;
    }

    inline bool operator != ( const wchar_t* other, text const& right )
    {
        return text(other) != right;
    }

    inline bool operator <= ( const wchar_t* other, text const& right )
    {
        return text(other) <= right;
    }

    inline bool operator >= ( const wchar_t* other, text const& right )
    {
        return text(other) >= right;
    }

    inline bool operator < ( const wchar_t* other, text const& right )
    {
        return text(other) < right;
    }

    inline bool operator > ( const wchar_t* other, text const& right )
    {
        return text(other) > right;
    }

    inline text::const_iterator::const_iterator()
        : m_index(0), m_container(nullptr)
    {
    }

    inline text::const_iterator::const_iterator( text const* container, int index )
        : m_index( index ), m_container( container )
    {
    }

    inline text::const_iterator& text::const_iterator::operator ++ ()
    {
        ++m_index;
        return *this;
    }

    inline text::const_iterator& text::const_iterator::operator -- ()
    {
        --m_index;
        return *this;
    }

    inline text::const_iterator text::const_iterator::operator ++ ( int )
    {
        const_iterator result = *this;
        ++m_index;
        return result;
    }

    inline text::const_iterator text::const_iterator::operator -- ( int )
    {
        const_iterator result = *this;
        --m_index;
        return result;
    }

    inline text::const_iterator& text::const_iterator::operator += ( int offset )
    {
        m_index += offset;
        return *this;
    }

    inline text::const_iterator& text::const_iterator::operator -= ( int offset )
    {
        m_index -= offset;
        return *this;
    }

    inline text::const_iterator text::const_iterator::operator + ( int offset ) const
    {
        return const_iterator( m_container, m_index + offset );
    }

    inline text::const_iterator text::const_iterator::operator - ( int offset ) const
    {
        return const_iterator( m_container, m_index - offset );
    }

    inline wchar_t text::const_iterator::operator * () const
    {
        assert_text_iterator_container_is_initialized( m_container );
        return (*m_container)[ index() ];
    }

    inline wchar_t text::const_iterator::operator [] ( int offset ) const
    {
        assert_text_iterator_container_is_initialized( m_container );
        return (*m_container)[ index() + offset ];
    }

    inline bool text::const_iterator::operator == ( text::const_iterator const& other ) const
    {
        return m_container == other.m_container && m_index == other.m_index;
    }

    inline bool text::const_iterator::operator != ( text::const_iterator const& other ) const
    {
        return m_container != other.m_container || m_index != other.m_index;
    }

    inline bool text::const_iterator::operator <= ( text::const_iterator const& other ) const
    {
        assert_text_iterator_container_match( m_container, other.m_container );
        return m_index <= other.m_index;
    }

    inline bool text::const_iterator::operator >= ( text::const_iterator const& other ) const
    {
        assert_text_iterator_container_match( m_container, other.m_container );
        return m_index >= other.m_index;
    }

    inline bool text::const_iterator::operator <  ( text::const_iterator const& other ) const
    {
        assert_text_iterator_container_match( m_container, other.m_container );
        return m_index < other.m_index;
    }

    inline bool text::const_iterator::operator >  ( text::const_iterator const& other ) const
    {
        assert_text_iterator_container_match( m_container, other.m_container );
        return m_index > other.m_index;
    }

    inline text::const_reverse_iterator text::const_iterator::reverse() const
    {
        return const_reverse_iterator( m_container, m_index );
    }

    inline int text::const_iterator::index() const
    {
        return m_index;
    }

    inline int operator - ( text::const_iterator const& left, text::const_iterator const& right )
    {
        assert_text_iterator_container_match( left.m_container, right.m_container );
        return left.m_index - right.m_index;
    }

    inline text::const_iterator operator + ( int offset, text::const_iterator const& iter )
    {
        return text::const_iterator( iter.m_container, offset + iter.m_index );
    }

    text::iterator::iterator()
    	: m_nonconst()
    {
    }

    text::iterator::iterator( text* container, int index )
        : base( container, index ), m_nonconst( container )
    {
    }

    inline wchar_t& text::iterator::operator * ()
    {
        assert_text_iterator_container_is_initialized( m_container );
        return (*m_nonconst)[ index() ];
    }

    inline wchar_t& text::iterator::operator [] ( int offset )
    {
        assert_text_iterator_container_is_initialized( m_container );
        return (*m_nonconst)[ index() + offset ];
    }

    inline text::const_reverse_iterator::const_reverse_iterator()
    {
    }

    inline text::const_reverse_iterator::const_reverse_iterator( text const* container, int index )
        : base( container, index )
    {
    }

    inline wchar_t text::const_reverse_iterator::operator * () const
    {
        assert_text_iterator_container_is_initialized( m_container );
        return (*m_container)[ index() ];
    }

    inline wchar_t text::const_reverse_iterator::operator [] ( int offset ) const
    {
        assert_text_iterator_container_is_initialized( m_container );
        return (*m_container)[ index() - offset ];
    }

    inline int text::const_reverse_iterator::index() const
    {
        return m_container->length() - 1 - m_index;
    }

    text::reverse_iterator::reverse_iterator()
    	: m_nonconst()
    {
    }

    text::reverse_iterator::reverse_iterator( text* container, int index )
        : base( container, index ), m_nonconst( container )
    {
    }

    inline wchar_t& text::reverse_iterator::operator * ()
    {
        assert_text_iterator_container_is_initialized( m_container );
        return (*m_nonconst)[ index() ];
    }

    inline wchar_t& text::reverse_iterator::operator [] ( int offset )
    {
        assert_text_iterator_container_is_initialized( m_container );
        return (*m_nonconst)[ index() - offset ];
    }
}
